/*************************************************************************************
*                                  SRL ENGINE                                        *
*                                                                                    *
*                                                                                    *
*  File : Program.cpp                                                                *
*  Author: SRL Killing                                                               *
*  Date: 25/02/10 20:30                                                              *
*  Description:                                                                      *
*                                                                                    *  
*  Licence :  LGPL                                                                   *
*                                                                                    *
*    Copyright (C) 2010 Pecatte Baptiste                                             *
*                                                                                    *
*    This program is free software; you can redistribute it and/or                   *
*    modify it under the terms of the GNU Lesser General Public                      *
*    License as published by the Free Software Foundation; either                    *
*    version 2.1 of the License, or (at your option) any later version.              *
*                                                                                    *
*    This program is distributed in the hope that it will be useful,                 *
*    but WITHOUT ANY WARRANTY; without even the implied warranty of                  *
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU                *
*    Lesser General Public License for more details.                                 *
*                                                                                    *
*    You should have received a copy of the GNU Lesser General Public                *
*    License along with this program; if not, write to the Free Software             *
*    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA    *
*                                                                                    *
**************************************************************************************/

#include <string>
#include <stdio.h>
#include <fstream>
#include <boost/format.hpp>
#include <GL/gl.h>
#include <GL/glu.h>

#include <SRL/Utils/Extensions.h>
#include <SRL/Utils/Core.h>

namespace srl {
    

	const GLenum glTextureNumber[] = {
		GL_TEXTURE0,
		GL_TEXTURE1,
		GL_TEXTURE2,
		GL_TEXTURE3,
		GL_TEXTURE4,
		GL_TEXTURE5,
		GL_TEXTURE6,
		GL_TEXTURE7,
		GL_TEXTURE8,
		GL_TEXTURE9,
		GL_TEXTURE10,
		GL_TEXTURE11,
		GL_TEXTURE12,
		GL_TEXTURE13,
		GL_TEXTURE14,
		GL_TEXTURE15,
		GL_TEXTURE16,
		GL_TEXTURE17,
		GL_TEXTURE18,
		GL_TEXTURE19,
		GL_TEXTURE20,
		GL_TEXTURE21,
		GL_TEXTURE22,
		GL_TEXTURE23,
		GL_TEXTURE24,
		GL_TEXTURE25,
		GL_TEXTURE26,
		GL_TEXTURE27,
		GL_TEXTURE28,
		GL_TEXTURE29,
		GL_TEXTURE30,
		GL_TEXTURE31
	};


    srl::Program* srl::Program::Current = NULL;
    
    Program::Program() {
        id = glCreateProgramObject();
    }
    Program::Program(srl::Shader *sh1, srl::Shader *sh2){
        id = glCreateProgramObject();
        if(sh1!=0) glAttachObject(id, sh1->id);
        if(sh2!=0) glAttachObject(id, sh2->id);
        if(sh1!=0 && sh2!=0){
            glLinkProgram(id);
            IsLinked();
        }
    }
    Program::~Program() {
        glDeleteObject(id);
    }
    
    // Shader functions
    void Program::Shader(srl::Shader *shader) {
        glAttachObject(id, shader->id);
    }
    void Program::DShader(srl::Shader *shader) {
        glDetachObject(id, shader->id);
    }
    
    // Link and use the program
    bool Program::Link() {
        glLinkProgram(id);
        return IsLinked();
    }
    bool Program::IsLinked() {
        GLint success;
        glGetProgramiv(id, GL_LINK_STATUS, &success);
        Linked = (success==GL_TRUE) ? true : false;
        return Linked;
    }
    void Program::Use() {
        glUseProgramObject(id);
        srl::Program::Current = this;
    }
    void Program::DUse() {
        glUseProgramObject(0);
        Program::Current = 0;
    }
    
    // Error Log
    std::string Program::Log() {
        GLint logsize;
        glGetProgramiv(id, GL_OBJECT_INFO_LOG_LENGTH_ARB, &logsize);
        if(logsize>0) {
		    char *log = NULL;
		    log = new char[logsize];
		    printf("Log size : %i", logsize);
		    glGetProgramInfoLog(id, logsize, (GLsizei*)300, log);
		    std::string str(log, logsize);
		    delete[] log;
		    return str;
        } else {
        	printf("\n<Erreur> Taille du log du fbo invalide : %i\n", logsize);
        	return std::string("");
        }
    }
    
    // Uniform variables functions
    
    //   Simples
    void Program::Uniform(std::string var, int val) {
        glUniform1i(glGetUniformLocation(id, var.c_str()), val);
    }
    void Program::Uniform(std::string var, float val) {
        glUniform1f(glGetUniformLocation(id, var.c_str()), val);
    }
    
    //   Vectors
    void Program::Uniform(std::string var, float val1, float val2) {
        glUniform2f(glGetUniformLocation(id, var.c_str()), val1, val2);
    }
    void Program::Uniform(std::string var, srl::Vector<float> val) {
        glUniform4f(glGetUniformLocation(id, var.c_str()), val.x, val.y, val.z, val.w);
    }
    void Program::Uniform(std::string var, float val1, float val2, float val3, float val4) {
        glUniform4f(glGetUniformLocation(id, var.c_str()), val1, val2, val3, val4);
    }
    void Program::Uniform(std::string var, int val1, int val2) {
        glUniform2i(glGetUniformLocation(id, var.c_str()), val1, val2);
    }
    void Program::Uniform(std::string var, srl::Vector<int> val) {
        glUniform4i(glGetUniformLocation(id, var.c_str()), val.x, val.y, val.z, val.w);
    }
    void Program::Uniform(std::string var, int val1, int val2, int val3, int val4) {
        glUniform4i(glGetUniformLocation(id, var.c_str()), val1, val2, val3, val4);
    }
    
    //    Matrix
    void Program::Uniform(std::string var, srl::Matrix<float> val) {
        glUniformMatrix4fv(glGetUniformLocation(id, var.c_str()), 1, 0, val.Get());
    }
    
    // Texture
    void Program::Uniform(std::string var, srl::Texture *val, int i) {
        glActiveTexture( glTextureNumber[i] );
        val->Bind();
        glUniform1i(glGetUniformLocation(id, var.c_str()), i);
    }
    void Program::Uniform(std::string var, srl::FrameBuffer *val, int i, int j) {
        glActiveTexture( glTextureNumber[i] );
        val->Bind(j);
        glUniform1i(glGetUniformLocation(id, var.c_str()), i);
    }
    bool Program::Uniform(std::string var, srl::FrameBuffer *val, int i) { // HERE
    	for(int j = 0; j < 10; j++) {
			if( val->GetTex(j) != 0 ) {
			
				glActiveTexture( glTextureNumber[i + j] );
		   		glBindTexture(GL_TEXTURE_2D, val->GetTex(j) );
				glUniform1i(glGetUniformLocation(id, (boost::format("%1%%2%") % var % j).str().c_str()), i + j);
			}
        }
        return true;
    }
    
    void Program::Set(srl::Program* newprog) {
    	newprog->Use();
    	srl::Program::Current = newprog;
    }
};
